import { CurrentUser, RealIp } from '@/decorators';
import {
  AssetAirdropDto,
  AssetBurnDto,
  AssetCirculationDto,
  AssetConsignmentDto,
  AssetCreateDto,
  AssetDeployDto,
  AssetDestroyDto,
  AssetDistributionDto,
  AssetFindDto,
  AssetHoldDto,
  AssetLockDto,
  AssetMintDto,
  AssetPreDto,
  AssetQueryDto,
  AssetReceiveDto,
  AssetRegistrationDto,
  AssetSaleDto,
  AssetSyncDto,
  AssetTargetedSaleDto,
  AssetTokensDto,
  AssetTransferDto,
  AssetUnLockDto,
  AssetUpdateDto,
  MarketBatchDownDto,
  MarketBatchUpDto,
  MarketDownDto, TokensDestroyDto
} from "@/dtos";
import { AssetService } from '@/services/asset.service';
import { halt, ok } from '@/utils';
import {
  Body,
  Controller,
  Delete,
  Get,
  Logger,
  Param,
  Patch,
  Post,
  Query,
} from '@nestjs/common';
import { Throttle, seconds } from '@nestjs/throttler';
import { BaseController } from './base.controller';
import { WishStatusEnum } from '@/enums/wish.enum';

@Controller('asset')
export class AssetController extends BaseController {
  constructor(private readonly assetService: AssetService) {
    super();
  }

  @Get('detail/:id')
  async getDetail(@Param('id') id: number, @CurrentUser('id') userId: number) {
    const data = (await this.assetService.findOne(
      {
        id,
        isDeleted: false,
        isTradable: true,
      },
      {
        id: true,
        title: true,
        isWish: true,
        image: true,
        limit: true,
        circulation: true,
        lowestPrice: true,
        highestPrice: true,
        publisher: {
          select: {
            id: true,
            name: true,
            logo: true,
            labels: true,
          },
        },
      },
    )) as any;
    if (!data) {
      return halt('数据异常');
    }
    const followCount = await this.prisma.userFollowPublisher.count({
      where: {
        publisherId: data.publisher.id,
      },
    });
    const isFollow = await this.prisma.userFollowPublisher.findFirst({
      where: {
        userId,
        publisherId: data.publisher.id,
      },
    });

    const max_wish = await this.prisma.wish.findFirst({
      select: {
        payPrice: true,
      },
      where: {
        assetId: id,
        status: WishStatusEnum.PREPAID,
      },
      orderBy: {
        payPrice: 'desc',
      },
    });

    return ok({
      ...data,
      lowestPrice: Number(data.lowestPrice),
      highestPrice: Number(data.highestPrice),
      followCount,
      isFollow: !!isFollow,
      max_wish: !!max_wish ? Number(max_wish.payPrice) : 0,
    });
  }

  @Post('follow/:id')
  async follow(@Param('id') id: number, @CurrentUser('id') userId: number) {
    const data = await this.prisma.userFollowAsset.findFirst({
      where: {
        userId,
        assetId: id,
      },
    });
    if (!!data) {
      await this.prisma.userFollowAsset.deleteMany({
        where: {
          userId,
          assetId: id,
        },
      });
    } else {
      await this.prisma.userFollowAsset.create({
        data: {
          userId,
          assetId: id,
        },
      });
    }
    return ok(true);
  }

  @Post('hold')
  async getUserHold(
    @Body() dto: AssetHoldDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(
      await this.assetService.hold({
        ...dto,
        userId,
      }),
    );
  }

  @Post('consignment')
  async getUserConsignment(
    @Body() dto: AssetConsignmentDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(
      await this.assetService.getUserConsignment({
        ...dto,
        userId,
      }),
    );
  }

  @Post('sale')
  async onSale(@Body() dto: AssetSaleDto, @CurrentUser('id') userId: number) {
    return ok(
      await this.assetService.onSale({
        ...dto,
        userId,
      }),
    );
  }

  @Delete('sale/:id')
  async offSale(@Param('id') id: number, @CurrentUser('id') userId: number) {
    return ok(await this.assetService.offSale(id, userId));
  }

  @Get('token/:id')
  async getToken(@Param('id') id: number, @CurrentUser('id') userId: number) {
    const data = (await this.prisma.token.findUnique({
      where: {
        id,
      },
      select: {
        id: true,
        no: true,
        price: true,
        bidPrice: true,
        ownerId: true,
        hash: true,
        source: true,
        status: true,
        asset: {
          select: {
            id: true,
            title: true,
            image: true,
            limit: true,
            circulation: true,
            detail: true,
            highestPrice: true,
            lowestPrice: true,
            isTradable: true,
            isUseable: true,
            isReceivable: true,
            isShipped: true,
            rightsType: true,
            rightsLimit: true,
            publisher: {
              select: {
                id: true,
                name: true,
                logo: true,
                labels: true,
              },
            },
            creator: {
              select: {
                id: true,
                name: true,
                logo: true,
                labels: true,
              },
            },
            album: {
              select: { id: true, title: true },
            },
          },
        },
      },
    })) as any;
    if (!data) {
      return halt('数据异常');
    }
    const followCount = await this.prisma.userFollowPublisher.count({
      where: {
        publisherId: data.asset.publisher.id,
      },
    });
    const isFollow = await this.prisma.userFollowPublisher.findFirst({
      where: {
        userId,
        publisherId: data.asset.publisher.id,
      },
    });
    const rightsCount = await this.prisma.userReceiveRights.count({
      where: {
        tokenId: id,
      },
    });
    return ok({
      ...data,
      price: Number(data.price),
      bidPrice: Number(data.bidPrice),
      asset: {
        ...data.asset,
        rightsCount,
        highestPrice: Number(data.asset?.highestPrice),
        lowestPrice: Number(data.asset?.lowestPrice),
      },
      followCount,
      isFollow: !!isFollow,
    });
  }

  @Post('tokens')
  async getTokens(
    @Body() dto: AssetTokensDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(
      await this.assetService.tokens({
        ...dto,
        userId,
      }),
    );
  }

  @Post('receive')
  @Throttle({
    short: {
      ttl: seconds(1),
      limit: 1,
    },
  })
  async receive(@Body() dto: AssetReceiveDto, @CurrentUser('id') id: number) {
    return ok(await this.assetService.receive(dto, id));
  }

  @Post('admin/query')
  async query(@Body() dto: AssetQueryDto) {
    return ok(await this.assetService.query(dto));
  }

  @Post('admin/find')
  async find(@Body() dto: AssetFindDto) {
    return ok(await this.assetService.findOne(dto.where, dto.select));
  }

  @Post('admin/create')
  async create(@Body() dto: AssetCreateDto) {
    return ok(await this.assetService.create(dto.data));
  }

  @Patch('admin/update')
  async update(@Body() dto: AssetUpdateDto) {
    return ok(await this.assetService.update(dto));
  }

  @Post('admin/deploy')
  async deploy(@Body() dto: AssetDeployDto) {
    return ok(await this.assetService.deploy(dto));
  }

  @Post('admin/mint')
  async mint(@Body() dto: AssetMintDto) {
    return ok(await this.assetService.mint(dto));
  }

  @Post('admin/sync')
  async sync(@Body() dto: AssetSyncDto) {
    return ok(await this.assetService.sync(dto));
  }

  @Post('admin/airdrop')
  async airdrop(@Body() dto: AssetAirdropDto, @CurrentUser('id') id: number) {
    if ([7, 20].includes(id)) {
      Logger.debug('无权限');
      return halt();
    }
    return ok(await this.assetService.airdrop(dto));
  }

  @Delete('admin/burn')
  async burn(@Query() dto: AssetBurnDto) {
    return ok(await this.assetService.burn(dto));
  }

  @Post('admin/targeted-sale')
  async targetedSale(
    @Body() dto: AssetTargetedSaleDto,
    @RealIp() ip: string,
    // @CurrentUser('id') id: number,
  ) {
    // if ([7, 20].includes(id)) {
    //   Logger.debug('无权限');
    //   return halt();
    // }
    return ok(
      await this.assetService.targetedSale({
        ...dto,
        ip,
      }),
    );
  }

  @Delete('admin/off-sale/:id')
  async adminOffSale(@Param('id') id: number) {
    const token = await this.prisma.token.findUnique({ where: { id } });
    return ok(await this.assetService.offSale(id, token.ownerId));
  }

  @Post('admin/transfer')
  async transfer(@Body() dto: AssetTransferDto) {
    return ok(await this.assetService.transfer(dto));
  }

  @Post('admin/distribution')
  async distribution(@Body() dto: AssetDistributionDto) {
    return ok(await this.assetService.distribution(dto));
  }

  @Post('admin/circulation')
  async circulation(@Body() dto: AssetCirculationDto) {
    return ok(await this.assetService.circulation(dto));
  }

  @Post('admin/hold')
  async hold(@Body() dto: AssetHoldDto) {
    return ok(await this.assetService.hold(dto));
  }

  @Post('admin/tokens')
  async tokens(@Body() dto: AssetTokensDto) {
    return ok(await this.assetService.tokens(dto));
  }

  @Post('admin/lock')
  async lock(@Body() dto: AssetLockDto) {
    return ok(await this.assetService.lock(dto));
  }

  @Post('admin/unlock')
  async unLock(@Body() dto: AssetUnLockDto) {
    return ok(await this.assetService.unLock(dto));
  }

  @Post('admin/pre')
  async pre(@Body() dto: AssetPreDto) {
    return ok(await this.assetService.pre(dto));
  }

  @Post('admin/registration')
  async registration(@Body() dto: AssetRegistrationDto) {
    return ok(await this.assetService.registration(dto));
  }

  @Post('admin/down')
  async down(@Body() dto: MarketDownDto) {
    return ok(await this.assetService.down(dto));
  }

  @Post('admin/batch-down')
  async batchDown(@Body() dto: MarketBatchDownDto) {
    return ok(await this.assetService.batchDown(dto));
  }

  @Post('admin/batch-down2')
  async batchDown2(@Body() dto: MarketBatchDownDto) {
    return ok(await this.assetService.batchDown2(dto));
  }

  @Post('admin/batch-up')
  async batchUp(@Body() dto: MarketBatchUpDto) {
    return ok(await this.assetService.batchUp(dto));
  }

  @Post('admin/destroy')
  async destroy(@Body() dto: AssetDestroyDto) {
    return ok(await this.assetService.destroy(dto));
  }

  @Get('token/destroy/:id')
  async tokenDestroy(
    @Param('id') tokenId: number,
    @CurrentUser('id') uid: number,
  ) {
    await this.redisLock('token_destroy', uid, 3);
    return ok(await this.assetService.tokenDestroy([tokenId], uid));
  }

  @Post('token/destroy')
  async tokensDestroy(
    @Body() dto: TokensDestroyDto,
    @CurrentUser('id') uid: number,
  ) {
    await this.redisLock('token_destroy', uid, 3);
    return ok(await this.assetService.tokenDestroy(dto.ids, uid));
  }

  @Post('tokens2')
  async getTokens2(
    @Body() dto: AssetTokensDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(
      await this.assetService.tokens2({
        ...dto,
        userId,
      }),
    );
  }
}
